/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { XTools } = require("../XTools");
const { X2DBeta } = require("./X2DInterface");
const { XLine } = require("./math/XLine");

function pathToNormal(x1, y1, x2, y2) {
    let nx = y2 - y1;
    let ny = -(x2 - x1);
    let len = Math.sqrt(nx * nx + ny * ny);
    return [nx / len, ny / len];
}

class XPath {
    constructor(pth, loop = true) {
        this.path_ = pth;
        this.loop_ = loop;
    }
    Interpolation(minl = 99999) {
        this.path_ = XPath.InterpolationImpl(this.path_, this.loop_, minl);
    }
    Smooth(minAngle = 90, expand = 5) {
        this.path_ = XPath.SmoothImpl(this.path_, this.loop_, minAngle, expand);
    }
    get path() {
        return this.path_;
    }
    set path(pth) {
        this.path_ = pth;
    }
    static CreateRaw(pth, loop = true) {
        return new XPath(pth, loop);
    }

    static InterpolationImpl(pth, loop = true, minl = 99999) {//段切割
        let lengths = [];
        if (minl == 99999) {
            for (let i = 0; i < pth.length + (loop ? 0 : -1); i++) {
                let j = (i + 1) % pth.length;
                let l = XTools.iDistance(pth[i][0] - pth[j][0], pth[i][1] - pth[j][1]);
                lengths.push(l);
                if (minl > l) minl = l;
            }
        }
        let ret = [];
        for (let i = 0; i < pth.length + (loop ? 0 : -1); i++) {
            let j = (i + 1) % pth.length;
            let l = XTools.iDistance(pth[i][0] - pth[j][0], pth[i][1] - pth[j][1]);
            // let l = lengths[i];
            ret.push(pth[i]);
            let cutt = parseInt(l / minl);
            for (let k = 1; k < cutt; k++) {
                let j = (i + 1) % pth.length;
                ret.push([
                    pth[i][0] + (pth[j][0] - pth[i][0]) * k / cutt,
                    pth[i][1] + (pth[j][1] - pth[i][1]) * k / cutt]);
            }
        }
        if (!loop) {
            ret.push(pth[pth.length - 1]);
        }
        return ret;
    }
    static SmoothImpl(pth, loop = true, minAngle = 90, expand = 5) {
        for (let m = (loop ? 100 : 0); m < pth.length + (loop ? 100 : -2); m++) {
            let i = m % pth.length;
            let j = (m + 1) % pth.length;
            let k = (m + 2) % pth.length;
            // console.log(i, j, k, pth.length)
            let t = XTools.IncludedAngle(...pth[i], ...pth[j], ...pth[k]);
            if (t < minAngle) {
                let d1 = XTools.iDistance(pth[i][0] - pth[j][0], pth[i][1] - pth[j][1]);
                let d2 = XTools.iDistance(pth[k][0] - pth[j][0], pth[k][1] - pth[j][1]);
                let n1 = [(pth[i][0] + pth[j][0]) / 2, (pth[i][1] + pth[j][1]) / 2];
                let n2 = [(pth[j][0] + pth[k][0]) / 2, (pth[j][1] + pth[k][1]) / 2];
                if (d1 < expand && d2 < expand) {
                    pth.splice(j, 1);
                    m -= 2;
                }
                else if (d1 < expand) {
                    pth.splice(j, 1, n2);
                    m--;
                }
                else if (d2 < expand) {
                    pth.splice(j, 1, n1);
                }
                else {
                    pth.splice(j, 1, n1, n2);
                }
                m--;
                if (!loop) {
                    if (m < 0) {
                        m = 0;
                    }
                }
            }
        }
        return pth;
    }
}

class XMesh {
    constructor() {
        this.inited_ = false;
    }
    static CreateByPath(pathPoints, width = 1, loop = false, cut = null, cutgo = 0) {
        let mesh = new XMesh();
        mesh.InitByPathEx(pathPoints, width, loop, cut, cutgo);
        return mesh;
    }
    InitByPath(pathPoints, width = 1, loop = false, rid = -1, cut = null) {
        let indices = [];
        let vs = [];
        let normal = [];
        for (let i = 0; i < pathPoints.length - 1; i++) {
            let p1 = pathPoints[i];
            let p2 = pathPoints[i + 1];
            normal.push(pathToNormal(...p1, ...p2));
        }
        if (loop) {
            normal.push(pathToNormal(...pathPoints[pathPoints.length - 1], ...pathPoints[0]));
        }
        else {
            normal.push(normal[normal.length - 1]);
        }

        for (let i = 0; i < pathPoints.length; i++) {
            let tx = pathPoints[i][0];
            let ty = pathPoints[i][1];
            let dx = normal[i][0] * width;
            let dy = normal[i][1] * width;
            vs.push(tx + dx, ty + dy, 0, 0);
            vs.push(tx - dx, ty - dy, 0, 0);
        }

        let ip = 0;
        for (let i = 0; i < pathPoints.length - 1; i++) {
            indices.push(0 + ip, 1 + ip, 2 + ip, 1 + ip, 3 + ip, 2 + ip);
            ip += 2;
        }
        if (loop) {
            indices.push(0 + ip, 1 + ip, 0, 1 + ip, 1, 0);
        }
        this.rid_ = (rid < 0 ? X2DBeta.WHITE_IMG_1X1 : rid);
        this.vertices_ = vs;
        this.indices_ = indices;
        this.inited_ = true;
    }
    InitByPathEx(pathPoints, width = 1, loop = false, cut = null, cutgo = 0) {
        width /= 2;
        let indices = [];
        let vs = [];
        let normal = [];
        let lines = [];
        let lengths = [];
        let totalLength = 0;
        let uvX = 0;
        let uvDW = 0;
        let uvY1 = 0;
        let uvY2 = 0;
        let segmentCount = (loop ? pathPoints.length : pathPoints.length - 1);
        for (let i = 0; i < pathPoints.length - 1; i++) {
            let p1 = pathPoints[i];
            let p2 = pathPoints[i + 1];
            normal.push(pathToNormal(...p1, ...p2));
            lines.push(XLine.Make(p1[0], p1[1], p2[0], p2[1]));
            let l = XTools.iDistance(p1[0] - p2[0], p1[1] - p2[1]);
            lengths.push(l);
            totalLength += l;
        }
        if (loop) {
            let p1 = pathPoints[pathPoints.length - 1];
            let p2 = pathPoints[0];
            normal.push(pathToNormal(...p1, ...p2));
            lines.push(XLine.Make(p1[0], p1[1], p2[0], p2[1]));
            let l = XTools.iDistance(p1[0] - p2[0], p1[1] - p2[1]);
            lengths.push(l);
            totalLength += l;
        }
        if (cut) {
            uvX = cut.u0;
            let tmpLength = width * 2 / cut.h * cut.w;
            uvDW = cut.w / cut.ww / tmpLength;//totalLength;
            uvY1 = cut.v0;
            uvY2 = cut.v3;
        }
        if (!loop) {
            normal.push(normal[normal.length - 1]);
            {
                let tx = pathPoints[0][0];
                let ty = pathPoints[0][1];
                let dx = normal[0][0] * width;
                let dy = normal[0][1] * width;
                vs.push(tx + dx, ty + dy, uvX, uvY1);
                vs.push(tx - dx, ty - dy, uvX, uvY2);
                uvX += uvDW * lengths[0];
            }
        }

        let begin = 0;
        let end = pathPoints.length - 2;
        if (loop) {
            cutgo = cutgo % pathPoints.length + pathPoints.length;
            begin = -1 + cutgo;
            end = pathPoints.length + cutgo;
        }
        let x1, y1, x2, y2;
        for (let j = begin; j < end; j++) {
            let i = (j + pathPoints.length) % pathPoints.length;
            let i2 = (i + 1) % pathPoints.length;
            let dx1 = normal[i][0] * width;
            let dy1 = normal[i][1] * width;
            let dx2 = normal[i2][0] * width;
            let dy2 = normal[i2][1] * width;
            let abc1 = XLine.Move(lines[i], dx1, dy1);
            let abc2 = XLine.Move(lines[i2], dx2, dy2);
            let dd1 = XLine.GetIntersection(...abc1, ...abc2);

            abc1 = XLine.Move(lines[i], -dx1, -dy1);
            abc2 = XLine.Move(lines[i2], -dx2, -dy2);
            let dd2 = XLine.GetIntersection(...abc1, ...abc2);

            if (dd1) {
                [x1, y1] = dd1;
            }
            else {
                x1 = pathPoints[i2][0] + dx2;
                y1 = pathPoints[i2][1] + dy2;
            }
            if (dd2) {
                [x2, y2] = dd2;
            }
            else {
                x2 = pathPoints[i2][0] - dx2;
                y2 = pathPoints[i2][1] - dy2;
            }
            vs.push(x1, y1, uvX, uvY1);
            vs.push(x2, y2, uvX, uvY2);
            if (cut && uvX >= cut.u1) {
            }
            else {
                uvX += uvDW * lengths[i2];
            }
            // X2DBeta.drawLine(x1, y1, x2, y2, 0xff00ff00, 1);
        }

        let ip = 0;
        for (let i = 0; i < segmentCount; i++) {
            indices.push(0 + ip, 1 + ip, 2 + ip, 1 + ip, 3 + ip, 2 + ip);
            ip += 2;
        }
        if (loop) {
            // indices.push(0 + ip, 1 + ip, 0, 1 + ip, 1, 0);
        }
        else {
            let tx = pathPoints[pathPoints.length - 1][0];
            let ty = pathPoints[pathPoints.length - 1][1];
            let dx = normal[pathPoints.length - 1][0] * width;
            let dy = normal[pathPoints.length - 1][1] * width;
            vs.push(tx + dx, ty + dy, uvX, uvY1);
            vs.push(tx - dx, ty - dy, uvX, uvY2);
            // uvX += uvDW;
        }
        this.rid_ = (cut ? cut.rid : X2DBeta.WHITE_IMG_1X1);
        this.vertices_ = vs;
        this.indices_ = indices;
        this.inited_ = true;
    }
    Draw(x = 0, y = 0, sw = 1, sh = 1, ra = 0, ox = 0, oy = 0, c = 0xffffffff) {
        if (!this.inited_) return;
        X2DBeta.DrawRawCutEx(this.rid_, this.vertices_, this.indices_, x, y, sw, sh, ra, ox, oy, c);
    }
}

module.exports = {
    XMesh,
    XPath
}